﻿using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.DataSourcesFile;
using ESRI.ArcGIS.DataSourcesOleDB;
using ESRI.ArcGIS.DataSourcesRaster;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Linq;

namespace AEFunction
{
    public struct ExcelField
    {
        public string field;
        public Type type;
    };

    public class ArcEngineFunction
    {
        /// <summary>
        /// 获取图层的FeatureClass
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        public static IFeatureClass GetLayerFeatureClass(ILayer layer)
        {
            IFeatureLayer featureLayer = layer as IFeatureLayer;
            return featureLayer.FeatureClass;
        }

        /// <summary>
        /// 获取全部图层
        /// </summary>
        /// <param name="axMapControl1"></param>
        /// <returns></returns>
        public static ILayer[] GetAllLayer(AxMapControl axMapControl1)
        {
            List<ILayer> layers = new List<ILayer>();
            for (int i = 0; i < axMapControl1.LayerCount; i++)
                layers.Add(axMapControl1.get_Layer(i));
            return layers.ToArray();
        }

        /// <summary>
        /// 从指定图层中获得指定字段的IField
        /// </summary>
        /// <param name="layer">指定图层</param>
        /// <param name="ShapeSelectedFields">指定字段</param>
        /// <returns></returns>
        public static IFields GetFields(ILayer layer, string[] ShapeSelectedFields)
        {
            IFields pFields = new FieldsClass();
            IFieldsEdit pFieldsEdit = (IFieldsEdit)pFields;
            IFeatureClass featureClass = GetLayerFeatureClass(layer);
            for (int i = 0; i < featureClass.Fields.FieldCount; i++)
            {
                IField f = featureClass.Fields.get_Field(i);
                if (ShapeSelectedFields.Contains(f.Name))
                    pFieldsEdit.AddField(f);
            }

            return pFields;

        }
        /// <summary>
        /// 创建点要素
        /// </summary>
        /// <param name="strShapeName">文件名</param>
        /// <param name="strShapeFolder">文件夹路径</param>
        /// <param name="fields">需要添加的IField类型的字段</param>
        /// <param name="inPutFileds">需要添加的string类型的字段</param>
        public static void CreatePointShapefile(ref string strShapeName, string strShapeFolder, IFields fields, List<ExcelField> inPutFileds)
        {
            //打开工作空间  


            IWorkspaceFactory pWSF = new ShapefileWorkspaceFactoryClass();
            IFeatureWorkspace pWS = (IFeatureWorkspace)pWSF.OpenFromFile(strShapeFolder, 0);

            IFields pFields = new FieldsClass();
            IFieldsEdit pFieldsEdit = (IFieldsEdit)pFields;

            //设置字段  
            IField pField = new FieldClass();
            IFieldEdit pFieldEdit = (IFieldEdit)pField;

            //创建类型为几何类型的字段  
            pFieldEdit.Name_2 = "shape";
            pFieldEdit.Type_2 = esriFieldType.esriFieldTypeGeometry;

            //为esriFieldTypeGeometry类型的字段创建几何定义，包括类型和空间参照   
            IGeometryDef pGeoDef = new GeometryDefClass();     //The geometry definition for the field if IsGeometry is TRUE.  
            IGeometryDefEdit pGeoDefEdit = (IGeometryDefEdit)pGeoDef;
            pGeoDefEdit.GeometryType_2 = esriGeometryType.esriGeometryPoint;
            pGeoDefEdit.SpatialReference_2 = new UnknownCoordinateSystemClass();

            pFieldEdit.GeometryDef_2 = pGeoDef;
            pFieldsEdit.AddField(pField);
            if(fields!=null)
            {
                for (int i = 0; i < fields.FieldCount; i++)
                {
                    IField fp = fields.get_Field(i);
                    pFieldsEdit.AddField(fp);
                }
            }


            foreach (ExcelField str in inPutFileds)
            {
                pField = new FieldClass();
                pFieldEdit = (IFieldEdit)pField;
                pFieldEdit.Name_2 = str.field;
                pFieldEdit.Type_2 = TypeToEsriType(str.type);

                pFieldsEdit.AddField(pField);
            }
            //创建shapefile  

            pWS.CreateFeatureClass(strShapeName, pFields, null, null, esriFeatureType.esriFTSimple, "shape", "");
        }
        /// <summary>
        /// 根据名字获取图层
        /// </summary>
        /// <param name="axMapControl1"></param>
        /// <param name="layerName"></param>
        /// <returns></returns>
        public static ILayer GetLayerByName(AxMapControl axMapControl1, string layerName)
        {
            for (int i = 0; i < axMapControl1.LayerCount; i++)
            {
                if (axMapControl1.get_Layer(i).Name == layerName)
                    return axMapControl1.get_Layer(i);
            }
            return null;
        }
        /// <summary>
        /// 根据名字获取图层
        /// </summary>
        /// <param name="axMapControl1"></param>
        /// <param name="layerName"></param>
        /// <returns></returns>
        public static ILayer GetLayerByName(ILayer[] layers, string layerName)
        {
            for (int i = 0; i < layers.Length; i++)
            {
                if (layers[i].Name == layerName)
                    return layers[i];
            }
            return null;
        }
        /// <summary>
        /// 获得字段在ILayer中的列索引
        /// </summary>
        /// <param name="layer"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        public static List<int> GetFieldIndexFromLayer(ILayer layer, string[] fields)
        {
            List<int> IndexList = new List<int>();
            IFields pFields = new FieldsClass();
            IFieldsEdit pFieldsEdit = (IFieldsEdit)pFields;
            IFeatureClass featureClass = GetLayerFeatureClass(layer);
            for (int i = 0; i < fields.Length; i++)
            {
                IndexList.Add(featureClass.Fields.FindField(fields[i]));
            }
            return IndexList;
        }
        /// <summary>
        /// 获得字段在ILayer中的列索引
        /// </summary>
        /// <param name="layer"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        public static List<int> GetExcelFieldIndexFromLayer(ILayer layer, ExcelField[] fields)
        {
            List<int> IndexList = new List<int>();
            IFields pFields = new FieldsClass();
            IFieldsEdit pFieldsEdit = (IFieldsEdit)pFields;
            IFeatureClass featureClass = GetLayerFeatureClass(layer);
            for (int i = 0; i < fields.Length; i++)
            {
                IndexList.Add(featureClass.Fields.FindField(fields[i].field));
            }
            return IndexList;
        }
        /// <summary>
        /// 根据图层字段创建一个只含字段的空DataTable
        /// </summary>
        /// <param name="pLayer"></param>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public static DataTable CreateDataTableByLayer(ILayer pLayer, string tableName)
        {
            //创建一个DataTable表
            DataTable pDataTable = new DataTable(tableName);
            //取得ITable接口
            ITable pTable = pLayer as ITable;
            IField pField = null;
            DataColumn pDataColumn;//System.Data.DataTable中列
            //根据每个字段的属性建立DataColumn对象
            for (int i = 0; i < pTable.Fields.FieldCount; i++)
            {
                pField = pTable.Fields.get_Field(i);
                //新建一个DataColumn并设置其属性
                pDataColumn = new DataColumn(pField.Name);
                if (pField.Name == pTable.OIDFieldName)//此列的每行须唯一
                {
                    pDataColumn.Unique = true;//字段值是否唯一
                }
                //字段值是否允许为空
                pDataColumn.AllowDBNull = pField.IsNullable;
                //字段别名
                pDataColumn.Caption = pField.AliasName;
                //字段数据类型
                pDataColumn.DataType = System.Type.GetType(ParseFieldType(pField.Type));
                //字段默认值
                pDataColumn.DefaultValue = pField.DefaultValue;
                //当字段为String类型是设置字段长度
                if (pField.VarType == 8)
                {
                    pDataColumn.MaxLength = pField.Length;
                }
                //字段添加到表中
                pDataTable.Columns.Add(pDataColumn);
                pField = null;
                pDataColumn = null;
            }
            return pDataTable;
        }

        /// <summary>
        /// 将GeoDatabase字段类型转换成.Net相应的数据类型
        /// </summary>
        /// <param name="fieldType">字段类型</param>
        /// <returns></returns>
        public static string ParseFieldType(esriFieldType fieldType)
        {
            switch (fieldType)
            {
                case esriFieldType.esriFieldTypeBlob:
                    return "System.String";
                case esriFieldType.esriFieldTypeDate:
                    return "System.DateTime";
                case esriFieldType.esriFieldTypeDouble:
                    return "System.Double";
                case esriFieldType.esriFieldTypeGeometry:
                    return "System.String";
                case esriFieldType.esriFieldTypeGlobalID:
                    return "System.String";
                case esriFieldType.esriFieldTypeGUID:
                    return "System.String";
                case esriFieldType.esriFieldTypeInteger:
                    return "System.Int32";
                case esriFieldType.esriFieldTypeOID:
                    return "System.String";
                case esriFieldType.esriFieldTypeRaster:
                    return "System.String";
                case esriFieldType.esriFieldTypeSingle:
                    return "System.Single";
                case esriFieldType.esriFieldTypeSmallInteger:
                    return "System.Int32";
                case esriFieldType.esriFieldTypeString:
                    return "System.String";
                default:
                    return "System.String";
            }
        }

        /// <summary>
        /// 填充DataTable中的数据
        /// </summary>
        /// <param name="pLayer"></param>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public static DataTable CreateDataTable(ILayer pLayer, string tableName)
        {
            IFeatureClass ipFClass = (pLayer as IFeatureLayer).FeatureClass;
            IDataset ipDataset = ipFClass as IDataset;
            IWorkspace ipWS = ipDataset.Workspace;
            IFDOToADOConnection fdoToadoConnection = new FdoAdoConnectionClass();

            ADODB.Connection adoConnection = new ADODB.Connection();

            fdoToadoConnection.Connect(ipWS, adoConnection);
            ADODB.Recordset adoRecordSet = new ADODB.Recordset();
            adoRecordSet.ActiveConnection = adoConnection;
            string sql = GetLayerSQL(pLayer);
            adoRecordSet.Open(sql, adoConnection, ADODB.CursorTypeEnum.adOpenDynamic, ADODB.LockTypeEnum.adLockOptimistic, 0);

            OleDbDataAdapter custDA = new OleDbDataAdapter();
            DataTable dtTerritories = new DataTable();
            custDA.Fill(dtTerritories, adoRecordSet);

            //FixShapeType(dtTerritories, GetLayerFeatureClass(pLayer).ShapeType;
            if (adoConnection.State == 1)
                adoConnection.Close();
            if (adoRecordSet.State == 1)
                adoRecordSet.Close();
            adoConnection = null;
            adoRecordSet = null;
            dtTerritories.Columns.Remove("Shape");
            return dtTerritories;
        }

        /// <summary>
        /// 获得图层的Shape类型
        /// </summary>
        /// <param name="pLayer">图层</param>
        /// <returns></returns>
        public static string getShapeType(ILayer pLayer)
        {
            IFeatureLayer pFeatLyr = (IFeatureLayer)pLayer;
            switch (pFeatLyr.FeatureClass.ShapeType)
            {
                case esriGeometryType.esriGeometryPoint:
                    return "Point";
                case esriGeometryType.esriGeometryPolyline:
                    return "Polyline";
                case esriGeometryType.esriGeometryPolygon:
                    return "Polygon";
                default:
                    return "";
            }
        }

        public static string GetLayerSQL(ILayer layer)
        {
            string sql = "select ";
            ITable pTable = layer as ITable;
            IField pField = null;
            //根据每个字段的属性建立DataColumn对象
            for (int i = 0; i < pTable.Fields.FieldCount; i++)
            {
                pField = pTable.Fields.get_Field(i);
                string field = pField.Name;
                //if (field == "Shape")
                //    field = "Geo;"
                if (i == pTable.Fields.FieldCount - 1)
                {
                    sql += field + "";
                }
                else
                {
                    sql += field + ",";
                }
            }
            sql += " from " + (layer as IFeatureLayer).FeatureClass.AliasName;
            return sql;
        }

        public static DataTable FixShapeType(DataTable dt, string type)
        {
            dt.Columns["Shape"].DataType = System.Type.GetType("System.String");
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                dt.Rows[i]["Shape"] = type;
            }
            return dt;
        }

        public static esriFieldType TypeToEsriType(Type t)
        {
            if (t.ToString() == "System.String")
                return esriFieldType.esriFieldTypeString;
            else if (t.ToString() == "System.Boolean")
                return esriFieldType.esriFieldTypeBlob;
            else if (t.ToString() == "System.Int32")
                return esriFieldType.esriFieldTypeInteger;
            else if (t.ToString() == "System.Double")
                return esriFieldType.esriFieldTypeDouble;
            else
                return esriFieldType.esriFieldTypeString;
        }
        //根据11*11范围内的栅格，并按照规则计算中心元胞的值
        public static double Calc11X11(int x, int y, IRasterLayer layer, IPixelBlock pixelBlock)
        {
            int count = 0;
            Dictionary<double, int> typeCount = new Dictionary<double, int>();
            typeCount.Add(5,0);
            typeCount.Add(6,0);
            typeCount.Add(7,0);
            typeCount.Add(8,0);
            typeCount.Add(12,0);
            for (int i = y - 5; i <= y + 5; i++)
            {
                for (int j = x - 5; j <= x + 5; j++)
                {
                    if (i < 0 || j < 0)
                        continue;
                    double value = GetPxValue(j, i, layer, pixelBlock);
                    if (value == 0)
                        continue;
                    count++;
                    if (typeCount.ContainsKey(value))
                        typeCount[value]++;
                    else
                    {
                        typeCount.Add(value, 1);
                    }
                }
            }
            double sum = 0;
            //商业用地减0.06，工矿仓储减0.25，住宅用地减0.35，公共管理减0.08，其他用地减0.26。
            foreach (int key in typeCount.Keys)
            {
                int cou = typeCount[key];
                double res1 = (double)cou / (double)count;
                if (key == 5)
                {
                    res1 -= 0.06;
                }
                else if (key == 6)
                {
                    res1 -= 0.25;
                }
                else if (key == 7)
                {
                    res1 -= 0.35;
                }
                else if (key == 8)
                {
                    res1 -= 0.08;
                }
                else if (key == 12)
                {
                    res1 -= 0.26;
                }

                sum += res1 * res1;

            }
            if (sum > 1 )
                System.Windows.Forms.MessageBox.Show("Test");
            return Math.Round(sum, 2);
        }

        public static double Calc101X101(int x, int y, IRasterLayer layer, IPixelBlock pixelBlock, System.Array pixels)
        {
            int count = 0;
            int height = pixels.GetLength(1);
            int width = pixels.GetLength(0);
            double nodata = Convert.ToDouble(pixels.GetValue(0,0));
            Dictionary<double, int> typeCount = new Dictionary<double, int>();
            for (int i = y - 50; i <= y + 50; i++)
            {
                for (int j = x - 50; j <= x + 50; j++)
                {
                    if (i < 0 || j < 0 || j >= width || i >= height)
                        continue;
                    double value = Convert.ToDouble(pixels.GetValue(j, i));
                    if (value == nodata)
                        continue;
                    count++;
                    if (typeCount.ContainsKey(value))
                        typeCount[value]++;
                    else
                    {
                        typeCount.Add(value, 1);
                    }
                }
            }
            double sum = 0;
            if (typeCount.Count==1)
            {
                return 0;
            }
            foreach (int key in typeCount.Keys)
            {
                int cou = typeCount[key];
                double res1 = (double)cou / (double)count;


                sum += res1 * Math.Log(Math.E, res1);
            }
            sum = -sum / Math.Log(Math.E, typeCount.Count);
            return Math.Round(sum, 2);
        }
        //根据11*11的规则，计算该图层所有栅格
        public static void ResetAllByCalc11X11(IRasterLayer layer)
        {
            IRasterProps pRP1 = layer.Raster as IRasterProps;

            //影像的宽度和高度
            double width = pRP1.Width;
            double height = pRP1.Height;


            //根据影像设定分辨率
            double cellWidth = pRP1.MeanCellSize().X;
            double cellHeight = pRP1.MeanCellSize().Y;
            IPnt pntSize = new PntClass();
            pntSize.SetCoords(cellWidth, cellHeight);
            IPixelBlock pixelBlock = layer.Raster.CreatePixelBlock(pntSize);
            double cellSize = cellWidth;
            IRaster2 rst2First = layer.Raster as IRaster2;
            IRaster raster = layer.Raster;
            IRasterProps raster1996 = raster as IRasterProps;

            //   指定像素块大小
            IPnt pntBlock = new PntClass();
            pntBlock.X = raster1996.Width;
            pntBlock.Y = raster1996.Height;
            //创建一个光标以给定像素块大小
            IRasterCursor rasterCursor = rst2First.CreateCursorEx(pntBlock);
            //创建像素块
            IPixelBlock3 pixelBlock3 = null;
            int blockWidth = 0;
            int blockHeight = 0;
            pixelBlock3 = rasterCursor.PixelBlock as IPixelBlock3;
            blockWidth = pixelBlock3.Width;
            blockHeight = pixelBlock3.Height;

            //指定平面的像素的数组
            System.Array pixels = (System.Array)pixelBlock3.get_PixelData(0);
            for (int i = 0; i < height; ++i)
            {
                for (int j = 0; j < width; ++j)
                {

                    double value1 = GetPxValue(j, i, layer, pixelBlock);
                    if (value1 == 0)
                        continue;
                    double result = Calc11X11(j, i, layer, pixelBlock);

                    pixels.SetValue(result, j, i);

                }
            }

            IRasterEdit rasterEdit = rst2First as IRasterEdit;
            if (rasterEdit.CanEdit())
            {

                pixelBlock3.set_PixelData(0, pixels);
                IPnt pnt = null;
                pnt = rasterCursor.TopLeft;
                rasterEdit.Write(pnt, (IPixelBlock)pixelBlock3);
                rasterEdit.Refresh();
            }
            //double dd = Convert.ToDouble(pixels.GetValue(55, 0));
            //IRaster pRaster = layer.Raster;
            //IRaster2 pRaster2 = pRaster as IRaster2;
            //ISaveAs pSaveAs = pRaster2 as ISaveAs;
            //pSaveAs.SaveAs("C:\\Users\\H\\Desktop\\数据处理\\"+layer.Name+"_result.tif", null, "TIFF");
            System.Runtime.InteropServices.Marshal.ReleaseComObject(rasterEdit);
        }

        public static double GetPxValue(int x, int y, IRasterLayer layer, IPixelBlock pixelBlock)
        {
            IPnt pnt = new PntClass();
            pnt.SetCoords(x, y);
            layer.Raster.Read(pnt, pixelBlock);
            object obj = pixelBlock.GetVal(0, 0, 0);
            return Convert.ToDouble(obj);
        }

        public static IRasterWorkspace2 CreateRasterWorkspace(string pathName)
        {
            // Create RasterWorkspace
            IWorkspaceFactory workspaceFactory = new RasterWorkspaceFactoryClass();

            return workspaceFactory.OpenFromFile(pathName, 0) as IRasterWorkspace2;
        }

        public static IRasterDataset CreateFileRasterDataset(IRasterWorkspace2 rasterWorkspace2, string fileName, IPoint originPoint, int width, int height, double cellsize, rstPixelType type, ISpatialReference sr,Array p)
        {
            // This function creates a new img file in the given workspace
            // and then assigns pixel values
            try
            {
                //IRasterDataset rasterDataset = null;
                //IPoint originPoint = new PointClass();
                //originPoint.PutCoords(0, 0);

                // Create the dataset
                //IRasterWorkspace2 rasterWorkspace2 = null;
                //rasterWorkspace2 = CreateRasterWorkspace(directoryName);

                IRasterDataset rasterDataset = rasterWorkspace2.CreateRasterDataset(fileName, "TIFF", originPoint, width, height, cellsize, cellsize, 1,type, sr, true);


                IRasterBandCollection rasterBands = (IRasterBandCollection)rasterDataset;

                IRasterBand rasterBand;

                IRasterProps rasterProps;

                rasterBand = rasterBands.Item(0);

                rasterProps = (IRasterProps)rasterBand;

                //Set NoData if necessary. For a multiband image, a NoData value needs to be set for each band.

                object nodatavalue = p.GetValue(0, 0);
                rasterProps.NoDataValue = nodatavalue;

                //Create a raster from the dataset.

                IRaster raster = rasterDataset.CreateDefaultRaster();

                //Create a pixel block using the weight and height of the raster dataset. 

                //If the raster dataset is large, a smaller pixel block should be used. 

                //Refer to the topic "How to access pixel data using a raster cursor".

                IPnt blocksize = new PntClass();

                blocksize.SetCoords(width, height);

                IPixelBlock3 pixelblock = raster.CreatePixelBlock(blocksize) as IPixelBlock3;

                //Populate some pixel values to the pixel block.

                //System.Array pixels1;

                //pixels1 = (System.Array)pixelblock.get_PixelData(0);

                //for (int i = 0; i < width; i++)
                //{ 
                //    for (int j = 0; j < height; j++)
                //    {
                //        pixels1.SetValue((float)(i*j%255), i, j);
                //    }
                //}  



                pixelblock.set_PixelData(0, p);

                //Define the location that the upper left corner of the pixel block is to write.

                IPnt upperLeft = new PntClass();

                upperLeft.SetCoords(0, 0);

                //Write the pixel block.

                IRasterEdit rasterEdit = (IRasterEdit)raster;
                if (rasterEdit.CanEdit())
                {
                    rasterEdit.Write(upperLeft, (IPixelBlock)pixelblock);

                    //Release rasterEdit explicitly.
                    rasterEdit.Refresh();

                }
                System.Runtime.InteropServices.Marshal.ReleaseComObject(rasterEdit);

                return rasterDataset;

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                return null;
            }
        }

    }
}
